/*

*/
#include "mpi.h"

#include "string.h"
#include <vector>

using namespace std;

#include "../core/morphology.h"

const int pattern_height = 40;
const int num_pattern = 10;
char* spattern[] = 
{ 
	    "0.bmp",
		"1.bmp",
		"2.bmp",
		"3.bmp",
		"4.bmp",
		"5.bmp",
		"6.bmp",
		"7.bmp",
		"8.bmp",
		"9.bmp"
};

const int num_samples = 2;
char* ssample[] = 
{
					"sample0.bmp",
					"sample2.bmp"
};

const int res_by_pattern = 10;
const double different_low_bound = 0.2;

int main (int argc, char **argv)
{
	int myrank, size;
	CImg sample[num_samples];
	CPattern pattern[num_pattern];
	CFindRes findres[num_pattern];

	for(int i = 0; i < num_samples; i++)
	{
		InitImg(ssample[i], &sample[i]);
	}
	for(int i = 0; i < num_pattern; i++)
	{
	  InitPattern(spattern[i], &pattern[i]);
	  InitFindRes(&findres[i], res_by_pattern);
	}

	const int FSIZE = 2 * sizeof(CFindRes);
	const int POINTSIZE = 2 * sizeof(CPoint);

	MPI_Status status;
	MPI_Init (&argc, &argv);
	MPI_Comm_rank (MPI_COMM_WORLD, &myrank);
	MPI_Comm_size(MPI_COMM_WORLD, &size);

	int buffstart;
	CImg loc_sample;
	for(int i = 0; i < num_samples; i++)
	{
		loc_sample.width = sample[i].width;
		if (myrank==0)    /* code for process zero */
		{
	        loc_sample.height = (int) (sample[i].height / size);
	        loc_sample.size = loc_sample.width * loc_sample.height;
	        buffstart = sample[i].height * sample[i].width * (size - 1) / size; 
		}
		else   /* code for other processes */
		{
			loc_sample.height = (int) (sample[i].height / size) + pattern_height - 1;
			loc_sample.size = loc_sample.width * loc_sample.height;
			buffstart = sample[i].height * sample[i].width * (myrank - 1) / size; 
		}

	    loc_sample.buff = new double[loc_sample.size];
	    for(int j = 0; j < loc_sample.size; j++) loc_sample.buff[j] = sample[i].buff[j + buffstart];

		Find2(&loc_sample, pattern, num_pattern, different_low_bound, findres);

        if(myrank == 0)
      	  {
      		  CFindRes buf_res;
      		  InitFindRes(&buf_res, res_by_pattern);
      		  vector<CPoint> p_res;
			  vector<double> dist_res;
			  for(int k = 0; k < num_pattern; k++)
			  {
				  for(int t = 0; t < findres[k].numres; t++)
				  {
					   p_res.push_back(findres[k].res_pos[t]);
					   dist_res.push_back(findres[k].res_dist[t]);
				  }

				  for(int j = 1; j < size; j++)
				  {
					  MPI_Recv(&buf_res, FSIZE, MPI_CHAR, j, k * (size+3) + j , MPI_COMM_WORLD, &status);
					  MPI_Recv(buf_res.res_dist, buf_res.numres, MPI_DOUBLE, j, k * (size+3) + j +1, MPI_COMM_WORLD, &status);
					  MPI_Recv(buf_res.res_pos, buf_res.numres * POINTSIZE, MPI_BYTE, j, k * (size+3) + j +2, MPI_COMM_WORLD, &status);

					  for(int t = 0; t < buf_res.numres; t++)
					  {
						  buf_res.res_pos[t].y += (ulong) (sample[i].height / size) * (j - 1);
						  p_res.push_back(buf_res.res_pos[t]);
						  dist_res.push_back(buf_res.res_dist[t]);
					  }			  
				  }

				  vector<double>::iterator jj = dist_res.begin();
				  printf("Pattern %d found in sample%d %d times:\n", k, i, (int)p_res.size());
				  for(vector<CPoint>::iterator ii = p_res.begin(); ii != p_res.end(); ii++)
				  {
					  printf("x = %ld, y = %ld, dist = %f\n", ii->x, ii->y, *jj);
					  jj++;
				  }
				  printf("%c", '\n');
				  
				  p_res.clear();
				  dist_res.clear();
			  }
			  DestroyFindRes(&buf_res);
		}
		else
		{
			  for(int k = 0; k < num_pattern; k++)
			  {
				  MPI_Send(&findres[k], FSIZE, MPI_CHAR, 0, k*(size+3) + myrank, MPI_COMM_WORLD);
				  MPI_Send(findres[k].res_dist, findres[k].numres, MPI_DOUBLE, 0, k*(size+3) + myrank + 1, MPI_COMM_WORLD);
				  MPI_Send(findres[k].res_pos, findres[k].numres * POINTSIZE, MPI_BYTE, 0, k*(size+3) + myrank + 2, MPI_COMM_WORLD);
			  }
		}

		delete [] loc_sample.buff;
	}

   MPI_Finalize();
}